#include "sys.h"
#include "net.h"
#include "log.h"

struct addrinfo* wd_gethostbyname(char* domain, uint32_t* ipaddrs, int count)
{
    int ret, i = 0;
    struct addrinfo *answer, hint, *curr;
    char ipstr[32];

    memset(&hint, 0, sizeof(hint));
    hint.ai_family = AF_INET;
    hint.ai_socktype = SOCK_STREAM;

    ret = getaddrinfo(domain, NULL, &hint, &answer);
    if (ret != 0) {
        log_error("getaddrinfo domain:%s error, %s", domain, gai_strerror(ret));
        return NULL;
    }
    for (curr = answer; curr != NULL; curr = curr->ai_next) {
        if (i >= count)
            break;
        inet_ntop(AF_INET, &(((struct sockaddr_in*)curr->ai_addr)->sin_addr), ipstr, sizeof(struct sockaddr_in));
        log_trace("IP:%s", ipstr);
        log_trace("ai_family:%d", curr->ai_family);
        log_trace("ai_socktype:%d", curr->ai_socktype);
        log_trace("ai_protocol:%d", curr->ai_protocol);
        log_trace("ai_canonname:%s", curr->ai_canonname);
        *(ipaddrs + i) = ((struct sockaddr_in*)curr->ai_addr)->sin_addr.s_addr;
        i++;
    }
    return answer;
}

uint32_t wd_ipaddr(char* ip_or_domain)
{
    struct addrinfo* answer;
    uint32_t ipaddr[4];
    struct in_addr addr;

    if (inet_pton(AF_INET, ip_or_domain, &addr) == 0) {
        answer = wd_gethostbyname(ip_or_domain, ipaddr, Array_Size(ipaddr));
        if (!answer) {
            return 0;
        }
        freeaddrinfo(answer);
        return ipaddr[0];
    } else {
        return inet_addr(ip_or_domain);
    }
    return 0;
}

void wd_ipaddr2(char *ip_or_domain, char *ipstr){
    struct in_addr addr;

    addr.s_addr = wd_ipaddr(ip_or_domain);

    inet_ntop(AF_INET, (void *)&addr, ipstr, 16);
}

int socket_new(int family, int type, int flag)
{
    int sk = 0;

    sk = socket(family, type, flag);
    if (sk < 0) {
        log_syserr("create socket error.");
        return -1;
    }
    return sk;
}

void sockaddr_set(struct sockaddr_in* addr, int family, uint16_t port, char* hostname)
{
    addr->sin_family = family;
    addr->sin_port = (port);
    addr->sin_addr.s_addr = wd_ipaddr(hostname);
}

void sock_sndtmo(int fd, int sec, int usec)
{
    struct timeval timeo;
    timeo.tv_sec = sec;
    timeo.tv_usec = usec;
    setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo, sizeof(timeo));
}

void sock_rcvtmo(int fd, int sec, int usec)
{
    struct timeval timeo;
    timeo.tv_sec = sec;
    timeo.tv_usec = usec;
    setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
}

static int _rt_ctl(int add_or_del, uint32_t dst, uint32_t gateway, uint32_t mask, char* dev)
{
    int fd = 0, ret = 0;
    struct rtentry rt = { 0 };
    struct sockaddr_in* addr;

    addr = (struct sockaddr_in*)&rt.rt_dst;
    addr->sin_family = AF_INET;
    addr->sin_addr.s_addr = dst;

    addr = (struct sockaddr_in*)&rt.rt_gateway;
    addr->sin_family = AF_INET;
    addr->sin_addr.s_addr = gateway;

    addr = (struct sockaddr_in*)&rt.rt_genmask;
    addr->sin_family = AF_INET;
    addr->sin_addr.s_addr = mask;

    rt.rt_metric = 0;
    rt.rt_flags = RTF_UP | RTF_HOST | RTF_GATEWAY;
    rt.rt_dev = dev;

    fd = socket_dgram_new();
    if (fd < 0)
        return -1;

    ret = ioctl(fd, (add_or_del ? SIOCADDRT : SIOCDELRT), &rt);
    if (ret != 0 && errno != EEXIST) {
        log_syserr("ioctl rt error.");
        close(fd);
        return -1;
    }
    close(fd);
    return 1;
}

int rt_add(uint32_t dst, uint32_t gateway, uint32_t mask, char* dev)
{
    return _rt_ctl(1, dst, gateway, mask, dev); //add :1; del :0
}
int rt_del(uint32_t dst, uint32_t gateway, uint32_t mask, char* dev)
{
    return _rt_ctl(0, dst, gateway, mask, dev); //add :1; del :0
}

int getifaddr(char* ifname, void* addr, char* type)
{
    int fd;
    struct sockaddr_in* sin;
    struct ifreq ifr;

    if(strlen(ifname) == 0){
        log_warn("ifname len is 0.");
        return 0;
    }

    if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        log_syserr("socket error.");
        return -1;
    }
    memset(&ifr, 0, sizeof(struct ifreq));
    strcpy(ifr.ifr_name, ifname);

    if (strcmp(type, "ip") == 0) {
        if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) {
            log_syserr("ioctl %s error.", ifname);
            return -1;
        }
        sin = (struct sockaddr_in*)&ifr.ifr_addr;
        memcpy(addr, &sin->sin_addr, sizeof(struct in_addr));
        return 1;
    } else if (!strcmp(type, "mac")) {
        if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
            log_syserr("ioctl %s error.", ifname);
            return -1;
        }
        memcpy(addr, ifr.ifr_hwaddr.sa_data, 6);
        return 1;
    }

    return 0;
}
void free_ifaces(struct ifconf* all)
{
    free(all->ifc_buf);
    free(all);
}
struct ifconf* get_ifaces()
{
    int sockfd;
    int len = 256;
    static struct ifconf* all = NULL;

    if (all)
        return all;
    all = malloc(sizeof(struct ifconf));
    all->ifc_len = len;
    all->ifc_buf = malloc(len);

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        log_syserr("IF create sock failed.");
        free(all->ifc_buf);
        free(all);
        return NULL;
    }
    if (0 > ioctl(sockfd, SIOCGIFCONF, all)) {
        log_syserr("ioctl err.");
        free(all->ifc_buf);
        free(all);
        close(sockfd);
        return NULL;
    }
    close(sockfd);
    return all;
}

unsigned long long monotonic_us(void)
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec * 1000000ULL + tv.tv_usec;
}
double cal_jitter(double* platencies, int count)
{
    int i;
    double total = 0;
    double val, meandelay, jitter;
    double* ptr;

    if(count < 2)
        return 0;

    ptr = platencies;
    for (i = 1; i < count; i++, ptr++) {
        val = *(ptr + 1) - *(ptr);
        val = val > 0 ? val : -val;
        total += val;
    }
    meandelay = total / (count - 1);
    jitter = meandelay;
    if (count < 3)
        goto out;

    total = 0;
    val = 0;
    ptr = platencies;
    for (i = 1; i < count; i++, ptr++) {
        val = *(ptr + 1) - *(ptr);
        val = val > 0 ? val : -val;
        jitter = (double)15 / 16.0 * jitter + (double)val / 16.0;
    }
out:
    jitter = jitter == 0 ? 0 : jitter;
    jitter = Positive(jitter);
    return jitter;
}

uint32_t get_random()
{
    time_t tm;
    tm = time(0);
    tm += (time_t)pthread_self();
    srand(tm);
    return (uint32_t)rand();
}

uint16_t port_random(uint16_t s)
{
    uint32_t ss;
    time_t tm;
    tm = time(0);
    if (s)
        srand(s);
    else
        srand(tm);
    ss = 1024 + (uint32_t)(60000.0 * rand() / (RAND_MAX + 1.0));
    return (uint16_t)ss;
}

void setnonblocking(int sock)
{
    int opts;
    opts = fcntl(sock, F_GETFL);
    if (opts < 0) {
        perror(" fcntl(sock,GETFL) ");
        return;
    }
    opts = opts | O_NONBLOCK;
    if (fcntl(sock, F_SETFL, opts) < 0) {
        perror(" fcntl(sock,SETFL,opts) ");
        return;
    }
}

struct udphdr* fill_udp_packet(struct udphdr* udp_hdr, uint16_t src_port, uint16_t dest_port, uint16_t blocksize)
{
    udp_hdr->source = src_port;
    udp_hdr->dest = dest_port;
    udp_hdr->len = htons(blocksize);
    udp_hdr->check = 0;
    return udp_hdr;
}

struct icmp* fill_icmp_packet(struct icmp* icmp_packet, int icmp_type, uint16_t icmp_sequ)
{
    icmp_packet->icmp_type = icmp_type;
    icmp_packet->icmp_code = 0;
    icmp_packet->icmp_cksum = 0;
    icmp_packet->icmp_id = ICMP_ID;
    icmp_packet->icmp_seq = icmp_sequ;
    *(uint32_t*)&icmp_packet->icmp_dun = monotonic_us();
    icmp_packet->icmp_cksum = icmp_check_sum((uint16_t*)icmp_packet, sizeof(struct icmp));
    return icmp_packet;
}

uint16_t icmp_check_sum(uint16_t* addr, int nleft)
{
    /*
	 * Our algorithm is simple, using a 32 bit accumulator,
	 * we add sequential 16 bit words to it, and at the end, fold
	 * back all the carry bits from the top 16 bits into the lower
	 * 16 bits.
	 */
    unsigned sum = 0;
    while (nleft > 1) {
        sum += *addr++;
        nleft -= 2;
    }

    /* Mop up an odd byte, if necessary */
    if (nleft == 1) {
        if (BB_LITTLE_ENDIAN) {
            sum += *(uint16_t*)addr;
        } else
            sum += *(uint16_t*)addr << 8;
    }

    /* Add back carry outs from top 16 bits to low 16 bits */
    sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
    sum += (sum >> 16); /* add carry */

    return (uint16_t)~sum;
}

void icmp_set_seq(struct icmp* icp, int seq)
{
    icp->icmp_cksum = 0;
    icp->icmp_seq = seq;
    *(uint32_t*)&icp->icmp_dun = monotonic_us();
    icp->icmp_cksum = icmp_check_sum((uint16_t*)icp, sizeof(struct icmp));
}

void set_ttl(int fd, int ttl)
{
    setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
}

void bps2str(float bps, char* str)
{
    if (bps < 1000) {
        sprintf(str, "%.2f bps", bps);
    } else if (bps > 1000 && bps <= 1000 * 1000) {
        sprintf(str, "%.2f Kbps", bps / 1000);
    } else if (bps > 1000 * 1000) {
        sprintf(str, "%.2f Mbps", bps / 1000 / 1000);
    }
}
void byte2str(uint32_t byte, char* str)
{
    float _byte = (float)byte;
    if (byte <= 1024) {
        sprintf(str, "%.2f B", _byte);
    } else if (_byte > 1024 && _byte <= 1024 * 1024) {
        sprintf(str, "%.2f KB", _byte / 1024);
    } else if (_byte > 1024 * 1024) {
        sprintf(str, "%.2f MB", _byte / 1024 / 1024);
    }
}

int check_gateway_interface(char* ifname)
{
    FILE* fp = NULL;
    char buf[256] = { 0 };
    char *delim = " \t", *saveptr = NULL;
    char *iface, *dest;
    int flag = 0;

    fp = fopen(PROC_NET_ROUTE, "r");
    if (!fp) {
        log_error("open " PROC_NET_ROUTE " file error.");
        return flag;
    }

    fgets(buf, sizeof(buf), fp); //skip header
    bzero(buf, sizeof(buf));
    while (fgets(buf, sizeof(buf), fp) != NULL) {
        if (strlen(buf) == 0) {
            continue;
        }
        iface = strtok_r(buf, delim, &saveptr);
        dest = strtok_r(NULL, delim, &saveptr);
        if (!strcmp(dest, "00000000")) {
            strcpy(ifname, iface);
            flag = 1;
            break;
        }
        bzero(buf, sizeof(buf));
    }
    fclose(fp);
    return flag;
}